/**
 * Sensor Network Development Kit for 1-Wire Network (SeN)
 * Version : 0.1
 * Revision date : 2008-10-13
 * Author: Dubravko Penezic, dpenezic@gmail.com
 * 
 * Based on T. Bitson, SimpleWeather, RainSensor.java, 2007
 */

package owSensor;

import java.util.Enumeration;

import owLib.OWCommPath;
import owLib.OWConf;
import owLib.OWDevice;

import com.dalsemi.onewire.*;
import com.dalsemi.onewire.adapter.*;
import com.dalsemi.onewire.container.*;

import utilityLib.*;

/**
 * DualCounter class implement method to read DS2423 and give possibility to work with 4Kb of memory
 * Version : 0.1.1
 * Revision date : 2008-11-01
 * 
 * @author Dubravko Penezic, dpenezic@gmail.com
 *
 */

public class DualCounter
{

	// class variables
	private DSPortAdapter adapter;
	private OneWireContainer1D counterDevice = null;
	private static boolean debugFlag = OWConf.debugFlag;
	private OWDevice device;
	private OWCommPath p;
	private MemoryDataTransformer mdt;

	// memory page from 0 to 11
	private String MAIN_MEMORY = "Main Memory";

	// memory page from 12 to 13
	private String WRITEC_MEMORY = "Memory with write cycle counter";

	// memory page from 14 to 15
	private String COUNTER_MEMORY = "Memory with externally triggered counter";

	/** 
	 * DualCounter method configure class parameters and create soft link to 1-Wire device
	 *  
	 * @param adapter
	 * @param device
	 */    
	public DualCounter(DSPortAdapter adapter, OWDevice device)
	{
		this.adapter = adapter;
		this.device = device;
		this.p = new OWCommPath(this.adapter, this.device);
		this.mdt = new MemoryDataTransformer();

		// get instances of the 1-wire devices
		counterDevice = new OneWireContainer1D(adapter, device.getDAddress());

		this.resetAllMemory();
	}

	/**
	 * resetAllMemory method write 0 value to all available memory pages (29 bytes of data in 16 pages from 0 to 15)
	 * 
	 */
	public void resetAllMemory(){

		byte[] value = new byte[29];

		for(int i=0; i < value.length; i++) value[i] = 0;

		for(int i=0; i < 16; i++) this.writeMemoryPage(i, value);

	}

	/**
	 * readMemoryPage method return byte array read from loc memory page 
	 * 
	 * Memory pages from 0-11 are standard memory pages without external function. Pages 12 and 13 have counter which add 
	 * one every time something is written in that memory, and page 14 and 15 are connected with external counter.
	 * 
	 * @param loc
	 * @return
	 */
	public byte[] readMemoryPage(int loc){

		byte[] value = {0};
		String memBank = "";

		MemoryBank      mb;
		PagedMemoryBank pg_mb;

		if(loc>-1 && loc<16){

			if(loc>-1 && loc<12) memBank = MAIN_MEMORY;
			if(loc>11 && loc<14) {
				memBank = WRITEC_MEMORY;
				loc = loc - 12;
			}
			if(loc>13 && loc<16) {
				memBank = COUNTER_MEMORY;
				loc = loc - 14;
			}

			p.activatePath();

			for(Enumeration bank_enum = counterDevice.getMemoryBanks();
			bank_enum.hasMoreElements(); )
			{
				// get the next memory bank, cast to MemoryBank
				mb = (MemoryBank)bank_enum.nextElement();

				if(mb.getBankDescription().equalsIgnoreCase(memBank)){
					if (mb instanceof PagedMemoryBank){
						pg_mb = (PagedMemoryBank)mb;

						value = new byte[pg_mb.getPageLength()];

						try {
							pg_mb.readPagePacket(loc, false, value, 0);
						} catch (OneWireIOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (OneWireException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}

		}

		p.deactivatePath();	

		return value;
	}

	/**
	 * writeMemoryPage method write up to 29 byte array to loc memory page
	 * 
	 * @param loc
	 * @param value
	 */
	public void writeMemoryPage(int loc, byte[] value){

		String memBank = "";

		MemoryBank      mb;
		PagedMemoryBank pg_mb;

		if(loc>-1 && loc<16){

			if(loc>-1 && loc<12) memBank = MAIN_MEMORY;
			if(loc>11 && loc<14) {
				memBank = WRITEC_MEMORY;
				loc = loc - 12;
			}
			if(loc>13 && loc<16) {
				memBank = COUNTER_MEMORY;
				loc = loc - 14;
			}

			p.activatePath();

			for(Enumeration bank_enum = counterDevice.getMemoryBanks();
			bank_enum.hasMoreElements(); )
			{
				// get the next memory bank, cast to MemoryBank
				mb = (MemoryBank)bank_enum.nextElement();

				if(mb.getBankDescription().equalsIgnoreCase(memBank)){
					if (mb instanceof PagedMemoryBank){
						pg_mb = (PagedMemoryBank)mb;
						if(pg_mb.getMaxPacketDataLength()>= value.length){
							try {
								pg_mb.writePagePacket(loc, value, 0, value.length);
							} catch (OneWireIOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							} catch (OneWireException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
				}
			}

		}

		p.deactivatePath();	

	}

	/**
	 * readAllCounters method read all counters and return it like 4 long value array
	 * order in array is Memory Counter 0,1 and then External Counter A and B
	 * 
	 * @return 4 long value array
	 */
	public long[] readAllCounters(){

		long[] value  = {0,0,0,0};

		try
		{
			if (debugFlag)
			{
				System.out.print("Counter: Device = " + counterDevice.getName());
				System.out.print("  ID = " + counterDevice.getAddressAsString() + "\n");
			}

			// activate path for this device
			p.activatePath();

			value[0] = counterDevice.readCounter(12);
			value[1] = counterDevice.readCounter(13);
			value[2] = counterDevice.readCounter(14);
			value[3] = counterDevice.readCounter(15);

		}
		catch (OneWireException e)
		{
			System.out.println("Error Reading Dual Counter: " + e);
		}

		// deactivate path for this device
		p.deactivatePath();

		return value;
	}

	/**
	 * readSwitchCounter method read external (switch) counter coun ("A", "B")
	 * 
	 * @param coun
	 * @return
	 */
	public long readSwitchCounter(String coun){
		long value = 0;

		try
		{
			if (debugFlag)
			{
				System.out.print("Counter: Device = " + counterDevice.getName());
				System.out.print("  ID = " + counterDevice.getAddressAsString() + "\n");
			}

			// activate path for this device
			p.activatePath();

			if(coun.equalsIgnoreCase("A")) value = counterDevice.readCounter(14);
			if(coun.equalsIgnoreCase("B")) value = counterDevice.readCounter(15);

		}
		catch (OneWireException e)
		{
			System.out.println("Error Reading Dual Counter: " + e);
		}

		// deactivate path for this device
		p.deactivatePath();

		return value;
	}

	/**
	 * readMemoryWriteCounter method read memory write counter for special all propose 2 page memory bank
	 * 
	 * @param page
	 * @return
	 */
	public long readMemoryWriteCounter(int page){
		long value = 0;

		try
		{
			if (debugFlag)
			{
				System.out.print("Counter: Device = " + counterDevice.getName());
				System.out.print("  ID = " + counterDevice.getAddressAsString() + "\n");
			}

			// activate path for this device
			p.activatePath();

			if(page == 0) value = counterDevice.readCounter(12);
			if(page == 1) value = counterDevice.readCounter(13);

		}
		catch (OneWireException e)
		{
			System.out.println("Error Reading Dual Counter: " + e);
		}

		// deactivate path for this device
		p.deactivatePath();

		return value;
	}

	/**
	 * readSwitchCounterWithMemory method return long array with follow value :
	 *  0 and 1 - time and counter from last read
	 *  2 and 3 - time and counter of current read
	 *  
	 *  Value of current reading together with time in milisecond are stored in first 12 bytes (8 for time and 4 for counter)
	 *  
	 * @param coun
	 * @return
	 */
	public long[] readSwitchCounterWithMemory(String coun){

		long[] rv = {0,0,0,0};

		String memBank = COUNTER_MEMORY;
		int loc = 0;
		byte[] value ;
		byte[] bcounter;
		byte[] btime;

		MemoryBank      mb;
		PagedMemoryBank pg_mb;

		if(coun.equalsIgnoreCase("B")) loc = 1;

		p.activatePath();

		for(Enumeration bank_enum = counterDevice.getMemoryBanks();
		bank_enum.hasMoreElements(); )
		{
			// get the next memory bank, cast to MemoryBank
			mb = (MemoryBank)bank_enum.nextElement();

			if(mb.getBankDescription().equalsIgnoreCase(memBank)){
				if (mb instanceof PagedMemoryBank){
					pg_mb = (PagedMemoryBank)mb;

					value = new byte[pg_mb.getPageLength()];
					bcounter = new byte[4];
					btime = new byte[8];
					
					try {
						// read memory
						pg_mb.readPagePacket(loc, false, value, 0);
						
						for(int i = 0; i < 12; i++){
							if(i<8) btime[i] = value[i];
							else bcounter[i-8] = value[i];
						}
						
						rv[0] = mdt.byteArrayToLong(btime);
						rv[1] = mdt.counterByteArrayToLong(bcounter);
						
						rv[2] = System.currentTimeMillis();
						rv[3] = counterDevice.readCounter(loc+14);
						
						value = new byte[12];
						btime = new byte[8];
						bcounter = new byte[4];
						
						btime = mdt.longToByteArray(rv[2]);
						bcounter = mdt.counterLongToByteArray(rv[3]);
						
						for(int i = 0; i < 12; i++){
							if(i<8) value[i] = btime[i];
							else value[i] = bcounter[i-8];
						}
						
						pg_mb.writePagePacket(loc, value, 0, value.length);
						
					} catch (OneWireIOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (OneWireException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}


		p.deactivatePath();	

		return rv;		
	}

}
